/**
 * Copyright (c) 2021 OceanBase
 * OceanBase CE is licensed under Mulan PubL v2.
 * You can use this software according to the terms and conditions of the Mulan PubL v2.
 * You may obtain a copy of Mulan PubL v2 at:
 *          http://license.coscl.org.cn/MulanPubL-2.0
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PubL v2 for more details.
 */

#ifndef OCEANBASE_STORAGE_OB_TABLET_SPLIT_MDS_USER_DATA
#define OCEANBASE_STORAGE_OB_TABLET_SPLIT_MDS_USER_DATA

#include <stdint.h>
#include "lib/container/ob_se_array.h"
#include "lib/utility/ob_print_utils.h"
#include "share/scn.h"
#include "common/ob_tablet_id.h"
#include "storage/blocksstable/ob_datum_rowkey.h"

namespace oceanbase
{
namespace common
{
class ObIAllocator;
}

namespace share
{
namespace schema
{
class ObTableSchema;
}
}

namespace storage
{
class ObITableReadInfo;
class ObLS;
class ObTabletHandle;
struct ObTabletSplitTscInfo;
class ObStorageSchema;

enum ObTabletSplitStatus {
  NO_SPLIT = 0,
  RANGE_PART_SPLIT_SRC,
  RANGE_PART_SPLIT_DST
};

class ObTabletSplitMdsUserData
{
public:
  OB_UNIS_VERSION_V(1);

public:
  ObTabletSplitMdsUserData() : allocator_(), auto_part_size_(OB_INVALID_SIZE), status_(NO_SPLIT), ref_tablet_ids_(), partkey_projector_(), end_partkey_(), storage_schema_() { }
  virtual ~ObTabletSplitMdsUserData() { reset(); }
  void reset();
  int assign(const ObTabletSplitMdsUserData &other);
  int init_no_split(const int64_t auto_part_size);
  int init_range_part_split_src(const ObIArray<ObTabletID> &dst_tablet_ids, const ObIArray<uint64_t> &partkey_projector, const share::schema::ObTableSchema &table_schema, const bool is_oracle_mode);
  int init_range_part_split_dst(const int64_t auto_part_size, const ObTabletID &src_tablet_id, const blocksstable::ObDatumRowkey &end_partkey);
  int modify_auto_part_size(const int64_t auto_part_size);
  bool is_split_src() const { return status_ == RANGE_PART_SPLIT_SRC; }
  bool is_split_dst() const { return status_ == RANGE_PART_SPLIT_DST; }

  // for data tablets and global local tablets, other tablets are likely to return OB_INVALID_SIZE
  int get_auto_part_size(int64_t &auto_part_size) const;

  // for split src data tablet
  int calc_split_dst(ObLS &ls, const blocksstable::ObDatumRowkey &rowkey, const int64_t abs_timeout_us, ObTabletID &dst_tablet_id) const;
  int calc_split_dst(
    const ObITableReadInfo &rowkey_read_info,
    const ObIArray<ObTabletSplitMdsUserData> &dst_split_datas,
    const blocksstable::ObDatumRowkey &rowkey,
    ObTabletID &dst_tablet_id,
    int64_t &dst_idx) const;

  // for split src data/lob/local index tablet
  int get_split_dst_tablet_ids(ObIArray<ObTabletID> &dst_tablet_ids) const;
  int get_split_dst_tablet_cnt(int64_t &split_cnt) const;
  int get_storage_schema(const ObStorageSchema *&storage_schema) const;

  // for split dst data/lob/local index tablet
  int get_split_src_tablet_id(ObTabletID &tablet_id) const;
  int get_split_src_tablet(ObLS &ls, ObTabletHandle &src_tablet_handle) const;

  // for split dst data tablet, return a shallow copy end_partkey
  int get_end_partkey(blocksstable::ObDatumRowkey &end_partkey);

  // for split dst data tablet
  int partkey_compare(const blocksstable::ObDatumRowkey &rowkey, const ObITableReadInfo &rowkey_read_info, const ObIArray<uint64_t> &partkey_projector, int &cmp_ret) const;

  // for split dst data/local index tablet
  int get_tsc_split_info(
    const ObTabletID &tablet_id,
    ObLS &ls,
    const int64_t abs_timeout_us,
    ObIAllocator &allocator,
    ObTabletSplitTscInfo &split_info);

  TO_STRING_KV(K_(auto_part_size), K_(status), K_(ref_tablet_ids), K_(partkey_projector), K_(end_partkey));

private:
  static int fill_min_max_datums(const int64_t datum_cnt, ObIAllocator &allocator, blocksstable::ObDatumRowkey &partkey);
  void print_ref_tablets_split_data(ObLS &ls) const;

private:
  ObArenaAllocator allocator_;
  int64_t auto_part_size_;
  uint64_t status_;
  ObSEArray<ObTabletID, 2> ref_tablet_ids_;
  ObSArray<uint64_t> partkey_projector_; // rowkey[partkey_projector_[i]] = ith part key
  blocksstable::ObDatumRowkey end_partkey_; // part key high bound of dst tablet, it's max for the rightmost tablet
  ObStorageSchema storage_schema_; // generated by table schema of split start, only valid for split src
private:
  DISALLOW_COPY_AND_ASSIGN(ObTabletSplitMdsUserData);
};

struct ReadSplitDataAutoPartSizeOp
{
  ReadSplitDataAutoPartSizeOp(int64_t &auto_part_size) : auto_part_size_(auto_part_size) {}
  int operator()(const ObTabletSplitMdsUserData &data)
  {
    return data.get_auto_part_size(auto_part_size_);
  }
  int64_t &auto_part_size_;
};

} // namespace storage
} // namespace oceanbase

#endif // OCEANBASE_STORAGE_OB_TABLET_SPLIT_MDS_USER_DATA
